/*
 * Permission.java
 *
 * Created on August 1, 2007, 10:03 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.auth.service.db;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import org.infoset.xml.Element;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.XMLException;
import org.milowski.db.DBCache;
import org.milowski.db.DBConnection;
import org.milowski.db.DBIterator;
import org.milowski.db.DBObject;
import org.milowski.db.DBQueryHandler;
import org.milowski.db.DBResultConstructor;
import org.milowski.db.DBUpdateHandler;
import org.milowski.db.Slot;

/**
 *
 * @author alex
 */
public class Group extends DBObject<AuthDB>  implements XMLObject
{

   Realm realm;
   String name;
   UUID uuid;
   Set<Role> roles;
   
   /** Creates a new instance of Permission */
   public Group(AuthDB db,int id,Realm realm,UUID uuid,String name)
      throws SQLException
   {
      super(db,id);
      this.realm = realm;
      this.name = name;
      this.uuid = uuid;
      this.roles = new TreeSet<Role>();
      init();
   }
   
   protected void init() 
      throws SQLException
   {
      DBConnection connection = db.getConnection();
      try {
         connection.query(AuthDB.GROUP_ROLES, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1, id);
            }
            public void onResults(ResultSet set) 
               throws SQLException
            {
               while (set.next()) {
                  int gid = set.getInt(1);
                  Role role = db.roleCache.get(gid);
                  roles.add(role);
               }
            }
         });
      } finally {
         db.release(connection);
      }
   }
   
   public void delete() 
      throws SQLException
   {
      DBConnection connection = db.getConnection();
      try {
         connection.deleteById(AuthDB.DELETE_GROUP_MEMBERS, id);
         connection.deleteById(AuthDB.DELETE_GROUP_ROLES, id);
         connection.deleteById(AuthDB.DELETE_GROUP, id);
         db.realmGroupCaches.get(realm).remove(id);
      } finally {
         db.release(connection);
      }
   }
   
   public Realm getRealm() {
      return realm;
   }

   public String getAlias()
   {
      return name;
   }

   public UUID getUUID()
   {
      return uuid;
   }
   
   public boolean addRole(final Role role)
      throws SQLException
   {
      if (roles.contains(role)) {
         return true;
      }
      DBConnection connection = db.getConnection();
      try {
         connection.update(AuthDB.CREATE_GROUP_ROLE, new DBUpdateHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setInt(2,role.getId());
            }
         });
      } finally {
         db.release(connection);
      }
      roles.add(role);
      return true;
   }
   
   public boolean removeRole(final Role role)
      throws SQLException
   {
      DBConnection connection = db.getConnection();
      try {
         return connection.update(AuthDB.DELETE_GROUP_ROLE, new DBUpdateHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setInt(2,role.getId());
            }
         })>0;
      } finally {
         db.release(connection);
      }
   }
   
   public boolean hasRole(Role role)
   {
      return roles.contains(role);
   }
   
   public Iterator<Role> getRoles() {
      return roles.iterator();
   }
   
   public boolean hasPermission(Permission permission)
      throws SQLException 
   {
      for (Role role : roles) {
         if (role.hasPermission(permission)) {
            return true;
         }
      }
      return false;
   }
   
   public void addMember(final RealmUser user)
      throws SQLException
   {
      DBConnection connection = db.getConnection();
      try {
         connection.update(AuthDB.CREATE_GROUP_MEMBER, new DBUpdateHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setInt(2,user.getId());
            }
         });
      } finally {
         db.release(connection);
      }
   }
   
   public boolean removeMember(final RealmUser user)
      throws SQLException
   {
      DBConnection connection = db.getConnection();
      try {
         return connection.update(AuthDB.DELETE_GROUP_MEMBER, new DBUpdateHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setInt(2,user.getId());
            }
         })>0;
      } finally {
         db.release(connection);
      }
   }
   
   public boolean hasMember(final RealmUser user) 
      throws SQLException
   {
      final Slot<Boolean> member = new Slot<Boolean>(false);
      DBConnection connection = db.getConnection();
      try {
         connection.query(AuthDB.GROUP_MEMBER, new DBQueryHandler() {
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1,id);
               s.setInt(2,user.getId());
            }
            public void onResults(ResultSet set)
               throws SQLException
            {
               member.set(set.next());
            }
         });
      } finally {
         db.release(connection);
      }
      return member.get();
   }
   
   public Iterator<RealmUser> getMembers() 
      throws SQLException
   {
      final Slot<Iterator<RealmUser>> result = new Slot<Iterator<RealmUser>>();
      final DBCache<UUID,RealmUser> cache = db.realmUserCaches.get(realm);
      final DBConnection connection = db.getConnection();
      try {
         connection.query(AuthDB.GROUP_MEMBERS, new DBQueryHandler() {
            public boolean shouldClose() { return false; }
            public void prepare(PreparedStatement s)
               throws SQLException
            {
               s.setInt(1, id);
            }
            public void onResults(ResultSet set) 
            {
               result.set(new DBIterator<RealmUser>(set,new DBResultConstructor<RealmUser>() {
                  public RealmUser newInstance(ResultSet set) 
                     throws SQLException
                  {
                     return cache.get(set.getInt(1));
                  }
               },db,connection));
            }
         });
      } catch (SQLException ex) {
         db.release(connection);
         throw ex;
      }
      return result.get();
   }
   
   public void refresh() 
      throws SQLException
   {
      roles.clear();
      init();
   }
   
   public boolean equals(Object obj) {
      return obj instanceof Group && ((Group)obj).getUUID().equals(uuid);
   }
   
   public void generate(ItemConstructor constructor,ItemDestination dest)
      throws XMLException
   {
      generate(constructor,dest,true);
   }
   
   public void generate(ItemConstructor constructor,ItemDestination dest,boolean contents)
      throws XMLException
   {
      Element top = constructor.createElement(XML.GROUP_NAME);
      top.setAttributeValue("id",uuid.toString());
      top.setAttributeValue("alias",name);
      top.setAttributeValue("realm",realm.getUUID().toString());
      dest.send(top);
      if (contents) {
         dest.send(constructor.createCharacters("\n"));
         dest.send(constructor.createElement(XML.ROLES_NAME));
         boolean first = true;
         for (Role r : roles) {
            if (first) {
               dest.send(constructor.createCharacters("\n"));
               first = false;
            }
            r.generate(constructor,dest,false);
            dest.send(constructor.createCharacters("\n"));
         }
         dest.send(constructor.createElementEnd(XML.ROLES_NAME));
         dest.send(constructor.createCharacters("\n"));
         dest.send(constructor.createElement(XML.USERS_NAME));
         try {
            first = true;
            Iterator<RealmUser> users = getMembers();
            while (users.hasNext()) {
               if (first) {
                  dest.send(constructor.createCharacters("\n"));
                  first = false;
               }
               users.next().generate(constructor,dest);
               dest.send(constructor.createCharacters("\n"));
            }
         } catch (SQLException ex) {
            throw new XMLException("Cannot get member list for group.",ex);
         }
         dest.send(constructor.createElementEnd(XML.USERS_NAME));
         dest.send(constructor.createCharacters("\n"));
      }
      dest.send(constructor.createElementEnd(XML.GROUP_NAME));
   }
   
}
